package tw.edu.tku.im.maze.database;


import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;


import tw.edu.tku.im.maze.MazeCell;
import tw.edu.tku.im.maze.GameState;
import tw.edu.tku.im.maze.MarkRep;
import tw.edu.tku.im.maze.MarkReq;
import tw.edu.tku.im.maze.Packet;

public class MarkProcessor extends PacketProcessor {

   private static String bytesToHex(byte[] bytes) {
      final char[] hexArray = 
         {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
      char[] hexChars = new char[bytes.length * 2];
      int v;
      for ( int j = 0; j < bytes.length; j++ ) {
          v = bytes[j] & 0xFF;
          hexChars[j * 2] = hexArray[v >>> 4];
          hexChars[j * 2 + 1] = hexArray[v & 0x0F];
      }
      return new String(hexChars);
  }
   
   private static byte[] hexToBytes(String hexString) {
      byte[] bytes = new byte[hexString.length() / 2];
      for(int i=0 ; i<bytes.length ; i++) {
         bytes[i] = (byte) Integer.parseInt(hexString.substring(2 * i, 2 * i + 2), 16);
       }
      return bytes;
    }
   
   @Override
   String acceptName() {
      return "mark";
   }
   
   private void testTimestamp() {
      try {
         Connection con = DriverManager.getConnection(address,user,passwd);
         con.setCatalog(database);
         CallableStatement cstmt = 
               con.prepareCall("{call testTimestamp(?,?)}");
         cstmt.setInt(1, 1);
         cstmt.registerOutParameter(2, Types.BINARY);
         cstmt.execute();
         byte[] timestamp = cstmt.getBytes(2);
         String str = MarkProcessor.bytesToHex(timestamp);
         for(int i=0; i<timestamp.length; i++) {
            System.out.printf("%X",timestamp[i]);
         }
         System.out.println();
         System.out.println(timestamp.toString());
         System.out.println(str);
         byte[] timestamp2 = MarkProcessor.hexToBytes(str);
         for(int i=0; i<timestamp2.length; i++) {
            System.out.printf("%X",timestamp2[i]);
         }
         if(timestamp.equals(timestamp2)) {
            System.out.println("timestamp==timestamp2");
         }
         System.out.println();
         System.out.println(timestamp.toString());
         cstmt = con.prepareCall("{call testTimestamp2(?)}",
               ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
         cstmt.setBytes(1, timestamp);
         cstmt.execute();
         ResultSet rs = cstmt.getResultSet();
         if(!rs.next()) {
            System.out.println("no rs");
         }else {
            if(rs.isLast()) {
               System.out.println(rs.getInt("markID"));
               System.out.println(rs.getInt("markX"));
               System.out.println(rs.getInt("markY"));
               System.out.println(rs.getBoolean("actions"));
               System.out.println(rs.getBytes("markTime").length);
               String ts = MarkProcessor.bytesToHex(rs.getBytes("markTime"));
               System.out.println(ts);
               byte[] tsbytes = MarkProcessor.hexToBytes(ts);
               for(int i=0; i<tsbytes.length; i++) {
                  System.out.printf("%X",tsbytes[i]);
               }
            }
         }
         
      } catch (SQLException e) {
         e.printStackTrace();
      }
   }
    
   private void test(MarkReq req) {
      List<MazeCell> markList = new ArrayList<MazeCell>();
      List<MazeCell> unmarkList = new ArrayList<MazeCell>();
      MazeCell[] marks=null, unmarks=null;
      try {
         Connection con = DriverManager.getConnection(address,user,passwd);
         con.setCatalog(database);
         CallableStatement cstmt = 
               con.prepareCall("{call usp_mark(?,?,?,?,?,?,?)}",
                  ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
         cstmt.setInt(1, req.getRoomID());
         cstmt.setString(2, req.getUserID());
         cstmt.setInt(3, req.getTicket());
         cstmt.setInt(4, req.getMark().getX());
         cstmt.setInt(5, req.getMark().getY());
         cstmt.setBoolean(6, req.isMarkAction());
         String lastTimestamp = req.getLastTimestamp();
         if(lastTimestamp!=null) {
            cstmt.setBytes(7, MarkProcessor.hexToBytes(lastTimestamp));
         }
         else {
            cstmt.setNull(7, Types.BINARY);
         }
         cstmt.execute();                  
         ResultSet rs = cstmt.getResultSet();

         String timestamp=null;
         while(rs.next()) {
            int x = rs.getInt("markX");
            int y = rs.getInt("markY");
            if(rs.getBoolean("actions")) {
               markList.add(new MazeCell(x, y));
            }
            else {
               unmarkList.add(new MazeCell(x, y));
            }
            if(rs.isLast()) { 
               timestamp = MarkProcessor.bytesToHex(rs.getBytes("markTime"));
            }
         }
         if(markList.size()!=0) {
            marks = new MazeCell[markList.size()];
            markList.toArray(marks);
         }
         if(unmarkList.size()!=0) {
            unmarks = new MazeCell[unmarkList.size()];
            unmarkList.toArray(unmarks);
         }
      } catch (SQLException e) {
         e.printStackTrace();
      }
      System.out.println("OK!!!");
   }
   
   private GameState updateGameState(int roomID, String userID, int ticket) {
      try {
         Connection con = DriverManager.getConnection(address, user, passwd);
         con.setCatalog(database);
         CallableStatement cstmt = con
               .prepareCall("{call usp_updateGameState(?,?,?,?,?) }");
         cstmt.setInt(1, roomID);
         cstmt.setString(2, userID);
         cstmt.setInt(3, ticket);
         cstmt.registerOutParameter(4, Types.BOOLEAN);
         cstmt.registerOutParameter(5, Types.INTEGER);
         cstmt.execute();
         boolean finished = cstmt.getBoolean(4);
         GameState gameState;
         if(finished) {
            int exp = cstmt.getInt(5);
            gameState = new GameState(finished, finished, exp);
         }
         else {
            gameState = new GameState(finished, finished);
         }
         return gameState;  
      } catch (SQLException e) {
         e.printStackTrace();
      }
      return null;
   }
   
   @Override
   Packet process(Packet packet) {
      MarkReq req = (MarkReq)packet;
      List<MazeCell> markList = new ArrayList<MazeCell>();
      List<MazeCell> unmarkList = new ArrayList<MazeCell>();
      MazeCell[] marks=null, unmarks=null;
      try {
         Connection con = DriverManager.getConnection(address,user,passwd);
         con.setCatalog(database);
         CallableStatement cstmt = 
               con.prepareCall("{call usp_mark(?,?,?,?,?)}",
                  ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
         cstmt.setInt(1, req.getRoomID());
         cstmt.setInt(2, req.getMark().getX());
         cstmt.setInt(3, req.getMark().getY());
         cstmt.setBoolean(4, req.isMarkAction());
         String lastTimestamp = req.getLastTimestamp();
         if(lastTimestamp!=null) {
            cstmt.setBytes(5, MarkProcessor.hexToBytes(lastTimestamp));
         }
         else {
            cstmt.setNull(5, Types.BINARY);
         }
         cstmt.execute();                  
         ResultSet rs = cstmt.getResultSet();

         String timestamp=null;
         while(rs.next()) {
            int x = rs.getInt("markX");
            int y = rs.getInt("markY");
            if(rs.getBoolean("actions")) {
               markList.add(new MazeCell(x, y));
            }
            else {
               unmarkList.add(new MazeCell(x, y));
            }
            if(rs.isLast()) { 
               timestamp = MarkProcessor.bytesToHex(rs.getBytes("markTime"));
            }
         }
         if(markList.size()!=0) {
            marks = new MazeCell[markList.size()];
            markList.toArray(marks);
         }
         if(unmarkList.size()!=0) {
            unmarks = new MazeCell[unmarkList.size()];
            unmarkList.toArray(unmarks);
         }
         GameState gameState = 
             updateGameState(req.getRoomID(), req.getUserID(), req.getTicket());
         return new MarkRep(timestamp, marks, unmarks, gameState);
      } catch (SQLException e) {
         e.printStackTrace();
      }
      //...... to do something
//      String timestamp = "2013-04-15 07:00:01.000";
//      MazeCell[] mark = new MazeCell[]{new MazeCell(1,2)};
//      MazeCell[] unmark = new MazeCell[]{new MazeCell(3,2), new MazeCell(3,4)};
//      GameState gameState = new GameState(true, true, 150);
//      MarkRep rep = new MarkRep(timestamp, mark, unmark, gameState);
      return null;
   }

//   public static void main(String[] args) {
//      final MarkReq req = new MarkReq(4, "test123", -1584041340, null, 
//            new MazeCell(31, 32), Boolean.TRUE);
//      new MarkProcessor().test(req);
//   }
   
}
